import json
import re

from django.core.mail import send_mail
from django.shortcuts import render
from django.http import JsonResponse

# Create your views here.
from django.views import View
from django_redis import get_redis_connection

from apps.carts.utils import merge_cookie_to_redis
from apps.goods.models import SKU
from apps.users.models import User, Address
from celery_tasks.send_email.tasks import celery_send_email
from utils.add_secret import generic_openid, check_access_token

"""
需求分析:
    根据页面的功能（从上到下，从左到右），哪些功能需要和后端配合完成
    如何确定哪些功能需要和后端进行交互呢：
        1.经验
        2.关注类似网站的相似功能  
"""
"""
判断用户名是否重复的功能
    前端：
        当用户输入用户名之后，失去焦点，发送一个axios（ajax）请求
    后端：
        请求：
            接收用户名
        业务逻辑：
            根据用户名查询数据库，如果查询结果数量=0，说明没有注册
            如果查询结果数量=1，说明有注册
        响应：
            返回JSON
            {code：0，count：0/1，errmsg：ok}
        
        路由   GET
        步骤：
            # 1，接收用户名
            # 2.查询数据库
            # 3.返回响应
"""


# 类视图用于检查用户名是否重复
class UsernameCountView(View):

    def get(self, request, username):
        # 1，接收用户名，对这个用户名进行判断
        # if not re.match('[a-zA-Z0-9_-]{5, 20}', username):
        #     return JsonResponse({"code": 200, "errmsg": "用户名不满足命名规则"})
        # 2.查询数据库
        count = User.objects.filter(username=username).count()
        # 3.返回响应
        return JsonResponse({"code": 0, "count": count, "errmsg": "ok"})


# 类视图用于检查手机号是否重复
class MobileCountView(View):

    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return JsonResponse({"code": 0, "count": count, "errmsg": "ok"})


"""
注册功能
前段： 当用户输入用户名、密码、确认密码、手机号、是否同意协议之后，会点击注册按钮
后端：
    请求：
        接受请求，获取数据
    业务逻辑： 
        验证数据。数据入库
    响应：
        JSON{‘code':0, 'errmsg':ok}
    
    路由：POST  register/
    步骤：
        # 1.接受请求（POST-----JSON）
        # 2.获取数据
        # 3.验证数据
        #     3.1 用户名、密码、确认密码、手机号、是否同意协议 都要有
        #     3.2 用户名满足规则，且不能重复
        #     3.3 密码满足规则
        #     3.4 确认密码和密码一致
        #     3.5 手机号满足规则且不能重复
        #     3.6 是否满足协议
        # 4.数据入库
        # 5.返回响应
"""


# 类视图用于注册
class RegisterView(View):

    def post(self, request):
        # 1.接受请求（POST - ----JSON）
        body_bytes = request.body
        body_str = body_bytes.decode()
        body_dict = json.loads(body_str)

        # 2.获取数据
        username = body_dict.get('username')
        password = body_dict.get('password')
        password2 = body_dict.get('password2')
        mobile = body_dict.get('mobile')
        allow = body_dict.get('allow')

        # 3.验证数据
        # 3.1 用户名、密码、确认密码、手机号、是否同意协议 都要有
        # all([xxx,xxx,xxx]), 只要all里的元素有None，False。all就返回False或者返回True
        if not all([username, password, password2, mobile, allow]):
            return JsonResponse({'code': 400, 'errmsg': '参数不全'})
        # 3.2 用户名满足规则，且不能重复
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):  # 正则中不要空格，不然总会有些莫名其妙的错误
            return JsonResponse({'code': 400, 'errmsg': '用户名不满足规则'})
        # 3.3 密码满足规则
        if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):  # 正则中不要空格，不然总会有些莫名其妙的错误
            return JsonResponse({'code': 400, 'errmsg': '密码设置不满足规则'})
        # 3.4 确认密码和密码一致
        if password != password2:
            return JsonResponse({'code': 400, 'errmsg': '两次输入不匹配'})
        # 3.5 手机号满足规则且不能重复
        if not re.match(r'^1[3-9]\d{9}$', mobile):  # 正则中不要空格，不然总会有些莫名其妙的错误
            return JsonResponse({'code': 400, 'errmsg': '电话号码有误'})
        # 3.6 是否满足协议
        if allow != True:
            return JsonResponse({'code': 400, 'errmsg': 'allow格式有误!'})
        # 4.数据入库
        # 该方式没有办法对密码进行加密
        # User.objects.create(username=username, password=password, mobile=mobile)
        # 该方法能实现密码的加密
        user = User.objects.create_user(username=username, password=password, mobile=mobile)

        # 系统提供了状态保持的方法
        from django.contrib.auth import login
        # request, user
        # 状态保持 ----- 已经登录的用户的状态保持
        # user 已经登录的用户信息
        login(request, user)

        # 5.返回响应
        return JsonResponse({'code': 0, "errmsg": 'ok'})


# 类视图实现状态保持
"""
如果需求是注册成功后即表示用户认证通过，那么此时可以在注册成功后实现状态保持
如果需求是注册成功后不表示用户认证通过，那么此时不用在注册成功后实现状态保持

实现状态保持主要有两种方式：
        在客户端存储信息使用Cookie
        在服务器端存储信息使用Session
"""


# 登录
"""
前段：
    当用户把用户名和密码输入完成之后，会点击登录按钮。这个时候前段会发送一个axios请求
    
后端：
    请求：
        接受数据，验证数据
    业务逻辑：
        验证用户名和密码是否正确，session
    响应：
        返回json数据
    
    步骤：
        # 1.接收数据
        # 2.验证数据
        # 3.验证用户名和密码是否正确
        # 4.状态保持
        # 5.判断是否记住登录
        # 6.返回响应

"""


class LoginView(View):

    def post(self, request):
        # 1.接收数据
        data = json.loads(request.body.decode())
        username = data.get('username')
        password = data.get('password')
        remembered = data.get('remembered')

        # 2.验证数据
        if not all([username, password]):
            return JsonResponse({'code': 400, "errmsg": "参数不全"})

        # 确定 我们是根据手机号查询 还是 根据用户名查询
        if re.match('1[3-9]\d{9}',username):
            User.USERNAME_FIELD='mobile'
        else:
            User.USERNAME_FIELD='username'

        # 3.验证用户名和密码是否正确
        from django.contrib.auth import authenticate
        # authenticate 传递用户名和密码
        # 如果用户名和密码正确则返回User信息
        # 如果不正确则返回None
        user = authenticate(username=username, password=password)

        if user is None:
            return JsonResponse({'code': 400, 'errmsg': '账户或者密码错误'})

        # 4.状态保持
        from django.contrib.auth import login
        login(request, user)

        # 5.判断是否记住登录
        if remembered:
            # 记住登录 -- 2周或者1个月
            request.session.set_expiry(None)
        else:
            # 不记住登录，浏览器关闭就过期
            request.session.set_expiry(0)

        # 6.返回响应
        response = JsonResponse({"code": 0, 'errmsg': 'ok'})

        # # 为了首页显示用户信息
        # response.set_cookie('username', username)
        #
        # # 合并购物车
        # response = merge_cookie_to_redis(request, response)

        return response


# 退出
"""
前端：
    当用户点击退出按钮的时候，前端发送一个axios delete请求
    
后端：
    请求
    业务逻辑：
        退出
    响应
        返回JSON数据

"""


from django.contrib.auth import logout


class LogoutView(View):

    def delete(self, request):
        logout(request)

        response = JsonResponse({'code': 0, "errmsg": "ok"})
        # 删除cookie信息
        response.delete_cookie('username')

        return response


# 用户中心，必须是登录用户
from utils.views import LoginRequireJSONMixin


class CenterView(LoginRequireJSONMixin, View):

    def get(self, request):
        # request.user就是已经登录的用户的信息
        # request.user是来源于中间件
        # 系统会进行判断，如果我们是登录用户，则可以获得登录用户对应的模型实例数据
        # 如果我们不是登录用户啊，则request.user则是一个匿名用户

        info_data = {'username': request.user.username,
                     'email': request.user.email,
                     'mobile': request.user.mobile,
                     'email_active':request.user.email_active
        }

        return JsonResponse({'code':0, 'errmsg': 'ok', 'info_data': info_data})


# 用户中心邮箱认证
"""
需求：保存邮箱地址
    发送一封激活邮件
    用户激活邮件
前端:
    当用户完成输入邮箱之后，点击保存，这个时候会发送axios请求。
后端：
    请求：
        接受请求，获取数据
    业务逻辑：
        保存邮箱地址，发送一封邮件
    响应： 
        JSON
    
    路由：
    PUT请求 email/
    步骤：
        # 1.接受请求
        # 2.获取数据
        # 3.保存邮箱地址
        # 4.发送激活邮件
        # 5.返回响应
        # 
"""


class EmailView(LoginRequireJSONMixin, View):

    def put(self, request):
        # 1.接受请求
        data = json.loads(request.body.decode())

        # 2.获取数据
        email = data.get('email')
        # 验证数据
        # 正则表达式

        # 3.保存邮箱地址
        user = request.user
        user.email = email
        user.save()

        # 4.发送激活邮件
        # subject, message, from_email, recipient_list,
        subject = '美多商城激活邮件'
        message = ""
        from_email = '美多商城<qi_rui_hua@163.com>'
        recipient_list = ['1286257618@qq.com']

        # 邮件的内容如果是html，这个时候使用html_message参数
        # 4.1 对a标签的链接数据进行加密处理
        # user_id =1
        # token
        token = generic_openid(request.user.id)

        # 4.2 组织我们的激活邮件
        verify_url = 'http://www.meiduo.site:8080/success_verify_email.html?token=%s' % token
        html_message ='<p>尊敬的用户您好！</p> '\
                      '<p>感谢您使用美多商城。</p> '\
                      '<p>您的邮箱为：%s 。请点击此链接激活您的邮箱：</p>'\
                      '<p><a href="%s">%s<a></p>' % (email, verify_url, verify_url)

        # send_mail(subject=subject,
        #           message=message,
        #           from_email=from_email,
        #           recipient_list=recipient_list,
        #           html_message=html_message)

        celery_send_email.delay(subject=subject,
                                message=message,
                                from_email=from_email,
                                recipient_list=recipient_list,
                                html_message=html_message)

        # 5.返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})

        pass


"""
1. 设置邮件服务器
    
    我们设置163邮箱服务器
    相当于我们开启了让163服务器帮我们发送邮件。同时设置了一些信息（特别是授权码）
    
2. 设置邮件发送的配置信息

    # 邮件发送相关
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_HOST = 'smtp.163.com'
    EMAIL_PORT = 25
    #发送邮件的邮箱
    EMAIL_HOST_USER = 'qi_rui_hua@163.com'
    #在邮箱中设置的客户端授权密码
    EMAIL_HOST_PASSWORD = '123456abc'
    #收件人看到的发件人
    EMAIL_FROM = '美多商城<qi_rui_hua@163.com>'
    
3. 调用send_email方法
    
"""


# 激活邮箱
"""
前端：
    用户点击激活链接，激活链接携带了token
后端：
    请求：
        接受请求，获取参数，验证参数
    业务逻辑：
        user_id，获取用户id查询参数，修改数据
    响应：
        返回JSON
    
    路由：
        emails/verification/ PUT
    步骤：
        # 1.接收请求
        # 2.获取参数
        # 3.验证参数
        # 4.获取user——id
        # 5.根据用户id查询数据
        # 6.修改数据
        # 7.返回json数据
        

"""


class EmailVerificationView(View):

    def put(self, request):
        # 1.接收请求
        params = request.GET

        # 2.获取参数
        token = params.get('token')

        # 3.验证参数
        if token is None:
            return JsonResponse({'code': 400, 'errmsg': '参数不全'})
        # 4.获取user——id
        else:
            user_id = check_access_token(token)
            if user_id is None:
                return JsonResponse({'code': 400, 'errmsg': '参数不全'})

        # 5.根据用户id查询数据
        user = User.objects.get(id=user_id)

        # 6.修改数据
        user.email_active = True
        user.save()

        # 7.返回json数据
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


# ====================================================================================
# 新增地址
"""
前端：
    当用户填写完成地址信息后，前端发送一个axios请求,会携带相关信息
后端：
    请求：
        接受请求，获取参数
    业务逻辑：
        数据入库
    响应：
        json
    
    路由：POST
    步骤：
        # 1.接受请求和数据
        # 2.验证数据
        # 3.数据入库
        # 4.返回响应
"""


# 新增地址
class AddAddress(LoginRequireJSONMixin, View):

    def post(self, request):
        # 1.接受请求和数据
        data = json.loads(request.body.decode())

        receiver = data.get('receiver')
        province_id = data.get('province_id')
        city_id = data.get('city_id')
        district_id = data.get('district_id')
        place = data.get('place')
        mobile = data.get('mobile')
        tel = data.get('tel')
        email = data.get('email')

        user = request.user

        # 2.验证数据
        # 2.1 验证必传参数
        # 2.2 省市区的id‘是否正确
        # 2.3 详细地址的长度
        # 2.4 手机号
        # 2.5 固定电话
        # 2.6 邮箱

        # 3.数据入库
        new_address = Address.objects.create(
            user=user,
            title=receiver,
            receiver=receiver,
            province_id=province_id,
            city_id=city_id,
            district_id=district_id,
            place=place,
            mobile=mobile,
            tel=tel,
            email=email)

        address = {
            "id": new_address.id,
            "title": new_address.title,
            "receiver": new_address.receiver,
            "province": new_address.province.name,
            "city": new_address.city.name,
            "district": new_address.district.name,
            "place": new_address.place,
            "mobile": new_address.mobile,
            "tel": new_address.tel,
            "email": new_address.email
        }

        # 4.返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'address': address})


# 展示地址
class AddressView(LoginRequireJSONMixin, View):

    def get(self, request):
        # 1. 查询指定数据
        user = request.user

        addresses = Address.objects.filter(user=user, is_deleted=False)

        # 2. 将对象数据转换为字典
        addresses_list = []
        for address in addresses:
            addresses_list.append({
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            })

        # 3. 返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'addresses': addresses_list})


# ====================================================================================================
# 浏览记录
"""
1.最近浏览记录，只有登录用户才可以访问，我们只记录登录用户的浏览记录
2.浏览记录应该有顺序
3.没有分页
①在用户访问商品详情的时候，添加浏览记录
②在个人中心，展示浏览记录

保存哪些数据?
    用户id，商品id，顺序（访问时间）
保存到哪里？
    一般保存到数据库（缺点：①慢②会频繁操作数据库）
    最好保存在redis中
    
    都可以，看具体安排
   
  
user_id, sku_id, 顺序    
redis:
    list
"""

"""
添加浏览记录:
    前端:
        当登录用户，访问某一个具体SKU页面的时候，发送一个axios请求。请求携带sku_id
    后端:
        请求:
            接受请求，获取请求参数，验证参数
        
        业务逻辑:
            链接redis，先去重，再保存到redis中，只保存五条记录
            
        响应:
            返回JSON
        
        路由: POST browse_history
        步骤:
            # 1.接收请求
            # 2.获取参数
            # 3.验证参数
            # 4.链接redis  list
            # 5.去重
            # 6，保存到redis中
            # 7.只保存5条数据
            # 8.返回响应
"""


class UserHistoryView(LoginRequireJSONMixin, View):

    def post(self, request):
        user = request.user
        # 1.接收请求
        data = json.loads(request. body.decode())

        # 2.获取参数
        sku_id = data.get('sku_id')

        # 3.验证参数
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return JsonResponse({'code': 400, 'errmsg': '没有此商品'})

        # 4.链接redis  list
        redis_cli = get_redis_connection('history')

        # 5.去重(先删除这个商品id，再添加就可以了)
        redis_cli.lrem('history_%s' % user.id, 0, sku_id)

        # 6，保存到redis中
        redis_cli.lpush('history_%s' % user.id, sku_id)

        # 7.只保存5条数据
        redis_cli.ltrim('history_%s' % user.id, 0, 4)

        # 8.返回响应
        return JsonResponse({'code':0, "errmsg": "ok"})

    def get(self, request):
        # 1.链接redis
        redis_cli = get_redis_connection('history')

        # 2.获取redis数据
        ids = redis_cli.lrange('history_%s' % request.user.id, 0 , 4)

        # 3.根据商品id进行数据查询
        history_list=[]
        for sku_id in ids:
            sku = SKU.objects.get(id=sku_id)
            # 4.将对象转换为字典
            history_list.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        # 5.返回JSON
        return JsonResponse({'code':0, "errmsg":"ok", "skus":history_list})


# 展示浏览记录
"""
    前端:
        用户在访问浏览请求的时候，发送axios请求，请求会携带session信息
    后端:
        请求:    
        业务逻辑:
            链接redis，获取redis数据，根据商品id进行数据查询
            
        响应: 将对象转换为字典
              
        路由: get
        步骤:
            # 1.链接redis
            # 2.获取redis数据
            # 3.根据商品id进行数据查询
            # 4.将对象转换为字典
            # 5.返回JSON
"""